package com.ingenico.tools.data.sampling;

import java.awt.geom.Path2D;
import java.io.IOException;

import com.ingenico.tools.nio.SampleBuffer;
import com.ingenico.tools.nio.channel.RandomReadableSampleChannel;

/**
 * This class used to be the only base for all Path Samplers however, it has a bug
 * because it does not parse all samples of the file because it always reposition
 * the reading stream on an offset based on a calculation made with a double...
 * 
 * Sometimes the offset grows more than the number of bytes and hence it misses some
 * bytes
 * 
 * Use of this sampler is deprecated and using AbstractDiscretePathSampler is recommended.
 * Code remains in the repository in case of regression
 * 
 * @author lvictor
 *
 */
public abstract class AbstractPathSampler implements PathSampler {
	private SampleBuffer samplesBuffer = null;

	protected abstract double getDoubleValue (SampleBuffer buffer);

	@Override
	public Path2D sampleToPath(final RandomReadableSampleChannel samplesChannel, double targetScale, int targetLength, long sampleOffset) throws IOException {
		final long samplesChannelSize = samplesChannel.size();
		/*
		 *  No need to do anything if
		 *  - the target length is null
		 *  -  the offset is beyond the byte channel's capacity
		 */
		if ( (targetLength == 0) || (samplesChannelSize <= sampleOffset) ) {
			return null;
		}

		// No need to actually re-sample anything if the target is not visible 
		if (targetLength < 1) {
			return null;
		}

		// Now, we create the path and - if required - the samples' buffer
		final Path2D path = new Path2D.Double();
		double samplesWindowLength = 1 / targetScale;
		if (samplesWindowLength < 1) {
//TODO: return null or arbitrarily fix to 1 ???
			samplesWindowLength = 1;
		}

		// Change the internal ByteBuffer capacity to be able to store all the 
		if ( (samplesBuffer == null) || (samplesBuffer.capacity() < samplesWindowLength ) ) {
// FIXME: out of memory should be caught or explicitly thrown
			samplesBuffer = samplesChannel.allocate((int)samplesWindowLength);
		}

		// We position our channel on the first sample
		double y;
		double x = sampleOffset;
		int availableSamples;
		samplesChannel.position(sampleOffset);
		path.reset();

		// initial chunk (moveTo() the initial point)
		samplesBuffer.clear();
		samplesBuffer.limit((int)samplesWindowLength);
		availableSamples = samplesChannel.read(samplesBuffer);

		// The sample buffer will be read back so we set its position and limit.
		samplesBuffer.position(0);
		if (availableSamples > 0) {
			samplesBuffer.limit(availableSamples);

			// Get representative value and sets the initial point.
			y = getDoubleValue(samplesBuffer);
			path.moveTo(x, y);
		} else {
			// When we reach the end of stream, we cannot read any further.
			// Let's return what we got
			return path;
		}

		// main chunk loop (lineTo() the other points)
		for (int i=1; i < targetLength; i++) {
			samplesBuffer.clear();
			samplesBuffer.limit((int)samplesWindowLength);

			// Jump to the first sample of the next window 
			x += samplesWindowLength;
			samplesChannel.position((long)x);
			availableSamples = samplesChannel.read(samplesBuffer);

			// The sample buffer will be read back so we set its position and limit.
			samplesBuffer.position(0);
			if (availableSamples > 0) {
				samplesBuffer.limit(availableSamples);

				// Get representative value and connect to the last point.
				y = getDoubleValue(samplesBuffer);
				path.lineTo(x, y);
			} else {
				// When we reach the end of stream, we cannot read any further.
				// Let's return what we got
				return path;
			}
		}

		return path;
	}

	@Override
	public String toString() {
		return getClass().getSimpleName();
	}

}
